/* **********************************************************************************
*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* **********************************************************************************/

using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.ComponentModel;
using System.Reflection;
using OfficeOpenXMLInterop;

namespace Theme_Builder
{
    // TODO: move this functionality to the utility project

    internal class Converters
    {
        public static Color ST_SystemColorValToColor(ST_SystemColorVal systemColorVal)
        {
            Color color = Color.Empty;

            switch (systemColorVal)
            {
                case ST_SystemColorVal.windowText:
                    color = SystemColors.WindowText;
                    break;
                case ST_SystemColorVal.window:
                    color = SystemColors.Window;
                    break;
            }

            return color;
        }

        public static byte[] ColorToByteRBG(Color color)
        {
            byte[] rgb = new byte[] { color.R, color.G, color.B };

            return rgb;
        }

        public static Color ByteRGBToColor(byte[] byteRGB)
        {
            if (byteRGB.Length != 3)
                return Color.Empty;

            int r = Convert.ToInt32(byteRGB[0]);
            int g = Convert.ToInt32(byteRGB[1]);
            int b = Convert.ToInt32(byteRGB[2]);

            Color color = Color.FromArgb(r, g, b);

            return color;
        }

        /// <summary>
        /// Parses the modifications in a ColorMods and adds them to the modifications in the scheme color
        /// </summary>
        /// <param name="schemeColor">The scheme color to modify</param>
        /// <param name="mods">Modifications to modify the scheme color by</param>
        public static void ParseModifications(ref OfficeOpenXMLInterop.CT_SchemeColor schemeColor, ColorMods mods)
        {
            // Alpha
            if (mods.alpha != 100)
            {
                schemeColor.alpha = new OfficeOpenXMLInterop.CT_PositiveFixedPercentage();
                schemeColor.alpha.val = (int)Math.Round(mods.alpha * 1000);
            }

            // Tint
            if (mods.tint != 100)
            {
                schemeColor.tint = new OfficeOpenXMLInterop.CT_PositiveFixedPercentage();
                schemeColor.tint.val = (int)Math.Round(mods.tint * 1000);
            }

            // Shade
            if (mods.shade != 100)
            {
                schemeColor.shade = new OfficeOpenXMLInterop.CT_PositiveFixedPercentage();
                schemeColor.shade.val = (int)Math.Round(mods.shade * 1000);
            }

            // Hue Mod
            if (mods.hueMod != 100)
            {
                schemeColor.hueMod = new OfficeOpenXMLInterop.CT_PositivePercentage();
                schemeColor.hueMod.val = (int)Math.Round(mods.hueMod * 1000);
            }

            // Sat Mod
            if (mods.satMod != 100)
            {
                schemeColor.satMod = new OfficeOpenXMLInterop.CT_Percentage();
                schemeColor.satMod.val = (int)Math.Round(mods.satMod * 1000);
            }

            // Lum Mod
            if (mods.lumMod != 100)
            {
                schemeColor.lumMod = new OfficeOpenXMLInterop.CT_Percentage();
                schemeColor.lumMod.val = (int)Math.Round(mods.lumMod * 1000);
            }

            // TODO: remove these?
            // Red Mod
            if (mods.redMod != 100)
            {
                schemeColor.redMod = new OfficeOpenXMLInterop.CT_Percentage();
                schemeColor.redMod.val = (int)Math.Round(mods.redMod * 1000);
            }

            // Green Mod
            if (mods.greenMod != 100)
            {
                schemeColor.greenMod = new OfficeOpenXMLInterop.CT_Percentage();
                schemeColor.greenMod.val = (int)Math.Round(mods.greenMod * 1000);
            }

            // Blue Mod
            if (mods.blueMod != 100)
            {
                schemeColor.blueMod = new OfficeOpenXMLInterop.CT_Percentage();
                schemeColor.blueMod.val = (int)Math.Round(mods.blueMod * 1000);
            }
        }


        public static ColorMods GetColorModifications(OfficeOpenXMLInterop.CT_SchemeColor schemeClr)
        {
            ColorMods mods = new ColorMods();

            if (schemeClr != null)
            {
                if (schemeClr.alpha != null)
                    mods.alpha = schemeClr.alpha.val / 1000.0;
                if (schemeClr.tint != null)
                    mods.tint = schemeClr.tint.val / 1000.0;
                if (schemeClr.shade != null)
                    mods.shade = schemeClr.shade.val / 1000.0;
                if (schemeClr.hueMod != null)
                    mods.hueMod = schemeClr.hueMod.val / 1000.0;
                if (schemeClr.satMod != null)
                    mods.satMod = schemeClr.satMod.val / 1000.0;
                if (schemeClr.lumMod != null)
                    mods.lumMod = schemeClr.lumMod.val / 1000.0;
                if (schemeClr.greenMod != null)
                    mods.greenMod = schemeClr.greenMod.val / 1000.0;
                if (schemeClr.redMod != null)
                    mods.redMod = schemeClr.redMod.val / 1000.0;
                if (schemeClr.blueMod != null)
                    mods.blueMod = schemeClr.blueMod.val / 1000.0;
            }

            return mods;
        }

        /// <summary>
        /// Modifies a color based on user defined modifications
        /// </summary>
        /// <param name="colorIn">Incoming color to modify.</param>
        /// <param name="mods">Values of modifications by which to modify the incoming color.</param>
        /// <returns></returns>
        public static Color ModifyColor(Color colorIn, ColorMods mods)
        {
            int a = (int)colorIn.A;
            int r = (int)colorIn.R;
            int g = (int)colorIn.G;
            int b = (int)colorIn.B;


            HSLHelper hslHelper = new HSLHelper( r, g, b );

            hslHelper.RotateHue(mods.hueMod / 100); // TODO: This may not be correct
            hslHelper.ModSat(mods.satMod / 100);
            hslHelper.ModLum(mods.lumMod / 100);

            hslHelper.GetRGB(ref r, ref g, ref b);

            // Adjust alpha
            if (mods.alpha != 100)
            {
                double pct = mods.alpha / 100;
                a = (int)(a * pct);
            }

            // Adjust tint
            //  40% tint == 40% original color & 60% white
            if (mods.tint != 100)
            {
                // A value of 255 255 255 is white

                double pctWhite = (100 - mods.tint) / 100;
                double pctClr = mods.tint / 100;

                r = (int)(((float)r * pctClr) + (255f * pctWhite));
                r = (r > 255 ? 255 : r);
                g = (int)(((float)g * pctClr) + (255f * pctWhite));
                g = (g > 255 ? 255 : g);
                b = (int)(((float)b * pctClr) + (255f * pctWhite));
                b = (b > 255 ? 255 : b);
            }

            // Adjust shade
            //  40% shade == 40% original color & 60% black
            if (mods.shade != 100)
            {
                // A value of 0 0 0 is black

                // TODO: this isn't entirely correct
                double pctBlack = mods.shade / 100d;

                r = (int)(r * pctBlack);
                g = (int)(g * pctBlack);
                b = (int)(b * pctBlack);
            }

            return Color.FromArgb(a, r, g, b);
        }

        /// <summary>
        /// Converts a string of the line dash properties into the actual type representation required
        /// for output to the package
        /// </summary>
        /// <param name="dash">A string representation of the dash preset type</param>
        /// <returns>The CT_PresetLineDashProperties which corresponds to the input dash type</returns>
        public static OfficeOpenXMLInterop.CT_PresetLineDashProperties GetLineDashProps(string dash)
        {
            OfficeOpenXMLInterop.CT_PresetLineDashProperties props = new OfficeOpenXMLInterop.CT_PresetLineDashProperties();

            switch (dash)
            {
                case "Solid":
                    props.val = OfficeOpenXMLInterop.ST_PresetLineDashVal.solid;
                    break;
                case "Dot":
                    props.val = OfficeOpenXMLInterop.ST_PresetLineDashVal.dot;
                    break;
                case "Dash":
                    props.val = OfficeOpenXMLInterop.ST_PresetLineDashVal.dash;
                    break;
                case "Large Dash":
                    props.val = OfficeOpenXMLInterop.ST_PresetLineDashVal.lgDash;
                    break;
                case "Dash Dot":
                    props.val = OfficeOpenXMLInterop.ST_PresetLineDashVal.dashDot;
                    break;
                case "Large Dash Dot":
                    props.val = OfficeOpenXMLInterop.ST_PresetLineDashVal.lgDashDot;
                    break;
                case "Large Dash Dot Dot":
                    props.val = OfficeOpenXMLInterop.ST_PresetLineDashVal.lgDashDotDot;
                    break;
                case "System Dash":
                    props.val = OfficeOpenXMLInterop.ST_PresetLineDashVal.sysDash;
                    break;
                case "System Dot":
                    props.val = OfficeOpenXMLInterop.ST_PresetLineDashVal.sysDot;
                    break;
                case "System Dash Dot":
                    props.val = OfficeOpenXMLInterop.ST_PresetLineDashVal.sysDashDot;
                    break;
                case "System Dash Dot Dot":
                    props.val = OfficeOpenXMLInterop.ST_PresetLineDashVal.sysDashDotDot;
                    break;
            }

            return props;
        }

        /// <summary>
        /// Converts the class type preset line dash properties into a string representation
        /// </summary>
        /// <param name="dash">The CT_PresetLineDashProperties</param>
        /// <returns>The string representation of the CT_PresetLineDashProperties</returns>
        public static string GetLineDashProps(OfficeOpenXMLInterop.CT_PresetLineDashProperties dash)
        {
            switch (dash.val)
            {
                case OfficeOpenXMLInterop.ST_PresetLineDashVal.dash:
                    return "Dash";
                case OfficeOpenXMLInterop.ST_PresetLineDashVal.dashDot:
                    return "Dash Dot";
                case OfficeOpenXMLInterop.ST_PresetLineDashVal.dot:
                    return "Dot";
                case OfficeOpenXMLInterop.ST_PresetLineDashVal.lgDash:
                    return "Large Dash";
                case OfficeOpenXMLInterop.ST_PresetLineDashVal.lgDashDot:
                    return "Large Dash Dot";
                case OfficeOpenXMLInterop.ST_PresetLineDashVal.lgDashDotDot:
                    return "Large Dash Dot Dot";
                case OfficeOpenXMLInterop.ST_PresetLineDashVal.solid:
                    return "Solid";
                case OfficeOpenXMLInterop.ST_PresetLineDashVal.sysDash:
                    return "System Dash";
                case OfficeOpenXMLInterop.ST_PresetLineDashVal.sysDashDot:
                    return "System Dash Dot";
                case OfficeOpenXMLInterop.ST_PresetLineDashVal.sysDashDotDot:
                    return "System Dash Dot Dot";
                case OfficeOpenXMLInterop.ST_PresetLineDashVal.sysDot:
                    return "System Dot";
            }

            return "Solid";
        }

        public static OfficeOpenXMLInterop.ST_CompoundLine GetCompoundLine(string compoundLine)
        {
            switch (compoundLine)
            {
                case "Single":
                    return OfficeOpenXMLInterop.ST_CompoundLine.sng;
                case "Double":
                    return OfficeOpenXMLInterop.ST_CompoundLine.dbl;
                case "Thick Thin":
                    return OfficeOpenXMLInterop.ST_CompoundLine.thickThin;
                case "Thin Thick":
                    return OfficeOpenXMLInterop.ST_CompoundLine.thinThick;
                case "Thin Thick Thin":
                    return OfficeOpenXMLInterop.ST_CompoundLine.tri;
            }

            return OfficeOpenXMLInterop.ST_CompoundLine.sng;
        }

        public static string GetCompoundLine(OfficeOpenXMLInterop.ST_CompoundLine compoundLine)
        {
            switch (compoundLine)
            {
                case OfficeOpenXMLInterop.ST_CompoundLine.dbl:
                    return "Double";
                case OfficeOpenXMLInterop.ST_CompoundLine.sng:
                    return "Single";
                case OfficeOpenXMLInterop.ST_CompoundLine.thickThin:
                    return "Thick Thin";
                case OfficeOpenXMLInterop.ST_CompoundLine.thinThick:
                    return "Thin Think";
                case OfficeOpenXMLInterop.ST_CompoundLine.tri:
                    return "Thin Thick Thin";
            }

            return "Single";
        }

        public static OfficeOpenXMLInterop.ST_LineCap GetLineCap(string cap)
        {
            switch (cap)
            {
                case "Flat":
                    return OfficeOpenXMLInterop.ST_LineCap.flat;
                case "Square":
                    return OfficeOpenXMLInterop.ST_LineCap.sq;
                case "Round":
                    return OfficeOpenXMLInterop.ST_LineCap.rnd;
            }

            return OfficeOpenXMLInterop.ST_LineCap.flat;
        }

        public static string GetLineCap(OfficeOpenXMLInterop.ST_LineCap cap)
        {
            switch (cap)
            {
                case OfficeOpenXMLInterop.ST_LineCap.flat:
                    return "Flat";
                case OfficeOpenXMLInterop.ST_LineCap.sq:
                    return "Square";
                case OfficeOpenXMLInterop.ST_LineCap.rnd:
                    return "Round";
            }

            return "Flat";
        }

        public static OfficeOpenXMLInterop.ST_PenAlignment GetAlignment(string alignment)
        {
            switch (alignment)
            {
                case "Center":
                    return OfficeOpenXMLInterop.ST_PenAlignment.ctr;
                case "Inset":
                    return OfficeOpenXMLInterop.ST_PenAlignment.@in;
            }

            return OfficeOpenXMLInterop.ST_PenAlignment.ctr;
        }

        public static string GetAlignment(OfficeOpenXMLInterop.ST_PenAlignment alignment)
        {
            switch (alignment)
            {
                case OfficeOpenXMLInterop.ST_PenAlignment.ctr:
                    return "Center";
                case OfficeOpenXMLInterop.ST_PenAlignment.@in:
                    return "Inset";
            }

            return "Center";
        }
    }
}
